package com.itxiuyixiu.tools.util.map;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author 黄磊
 * @date 2020/4/21
 */
public class ScheduleHashMapImpl<K, V> implements ScheduleHashMap<K, V> {
    class Value {
        V value;
        Instant timeTemp;

        public Value(V value, Instant timeTemp) {
            this.value = value;
            this.timeTemp = timeTemp;
        }

        @Override
        public String toString() {
            return "Value{" +
                    "value=" + value +
                    ", timeTemp=" + timeTemp +
                    '}';
        }
    }

    private ConcurrentHashMap<K, Value> map;

    public ScheduleHashMapImpl() {
        this.map = new ConcurrentHashMap<>();
        startSchedule();
    }

    public ScheduleHashMapImpl(Map<? extends K, ? extends Value> m) {
        this.map = new ConcurrentHashMap<>(m);
        startSchedule();
    }

    public ScheduleHashMapImpl(int initialCapacity) {
        this.map = new ConcurrentHashMap<>(initialCapacity);
        startSchedule();
    }

    public ScheduleHashMapImpl(int initialCapacity, float loadFactor) {
        this.map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
        startSchedule();
    }

    public ScheduleHashMapImpl(int initialCapacity, float loadFactor, int concurrencyLevel) {
        this.map = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
        startSchedule();
    }

    private void startSchedule() {
        ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutor.scheduleWithFixedDelay(() -> {
            for (Map.Entry<K, Value> entry : map.entrySet()) {
                if (Instant.now().isAfter(entry.getValue().timeTemp)) {
                    System.out.println(entry.getValue().value);
                    map.remove(entry.getKey());
                }
            }
        }, 0, 1, TimeUnit.SECONDS);
    }


    @Override
    public V put(K key, V value, long time, TimeUnit timeUnit) {
        Value result = map.put(key, new Value(value, Instant.ofEpochMilli(Instant.now().toEpochMilli() + timeUnit.toMillis(time))));
        if (result == null) {
            return null;
        }
        return result.value;
    }

    @Override
    public V get(K key) {
        Value result = map.get(key);
        if (result == null) {
            return null;
        }
        return result.value;
    }
}
